package com.hnd.springframework.beans.factory.support;

import cn.hutool.core.bean.BeanUtil;
import com.hnd.springframework.beans.BeansException;
import com.hnd.springframework.beans.PropertyValue;
import com.hnd.springframework.beans.PropertyValues;
import com.hnd.springframework.beans.factory.config.BeanDefinition;
import com.hnd.springframework.beans.factory.config.BeanReference;
import com.hnd.springframework.beans.factory.support.InstantiationStrategyImpl.CglibSubclassingInstantiationStrategy;

import java.lang.reflect.Constructor;

/**
 * 负责创建bean，有参实例化，无参实例化，属性的注入
 */
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {
    private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

    @Override
    Object createBean(String beanName, BeanDefinition beanDefinition,Object... args) throws BeansException {

        Object beanInstance = createBeanInstance(beanName, beanDefinition, args);

        //属性注入
        applyPropertyValues(beanInstance, beanDefinition);

        return beanInstance;
    }

    private Object createBeanInstance(String beanName, BeanDefinition beanDefinition,Object... args) {
        Class clazz = beanDefinition.getBeanClass();
        if (args.length == 0) {
            return instantiationStrategy.instantiate(beanDefinition, null, beanName, args);
        }

        Constructor[] constructors = clazz.getDeclaredConstructors();
        Constructor constructorToUse = null;
        for (Constructor constructor : constructors) {
            if (constructor.getParameterTypes().length == args.length) {
                constructorToUse = constructor;
                break;
            }
        }

        return instantiationStrategy.instantiate(beanDefinition, constructorToUse, beanName, args);
    }

    private void applyPropertyValues(Object bean, BeanDefinition beanDefinition) {
        PropertyValue[] propertyValues = beanDefinition.getPropertyValues().getPropertyValues();
        if (propertyValues.length == 0) {
            return;
        }
        for (PropertyValue propertyValue : propertyValues) {
            Object value = propertyValue.getValue();
            if (propertyValue.getValue() instanceof BeanReference) {
                value = getBean(propertyValue.getName());
            }
            BeanUtil.setFieldValue(bean,propertyValue.getName(),value);
        }
    }
}
